<?php

!defined('IN_VIFRAME') && die('Access Denied.');

Vi::checkClassExist('Factory');

class Filter extends TLib {

	const
		FILTER_OBJECT   = 'OBJECT',
		FILTER_FUNCTION = 'FUNCTION',
		FILTER_CLASS    = 'CLASS',
		FILTER_LIB      = 'LIB',
		FILTER_MERGE    = 'MERGE';

	public static
		$Filters   = array();

	protected static
		$_Applied   = array();

	protected static
		$_SuperKeys = array();
	
	public static function decodeKey($key) {
		return explode('.', $key);
	}

	public static function bind($key, $filter, $priority = 10, $superKey = null, $type = VI_HOOK_TYPE) {
		if (!is_numeric($priority))
			$priority = 10;
		if ($priority > 100)
			$priority = 100;

		if (!empty($superKey)) {
			self::$_SuperKeys[$superKey] = array($key, $priority, $filter);
		}
		
		self::$Filters[$key][$priority][$filter] = array(
			0 => $type,
			1 => $filter
		);

		return true;
	}

	public static function bindObject($key, $filter, $priority = 10, 
			$superKey = null) {

		return (self::bind($key, $filter, $priority, $superKey, self::FILTER_OBJECT));
	}
	
	public static function bindFunction($key, $filter, $priority = 10, 
			$superKey = null) {

		return (self::bind($key, $filter, $priority, $superKey, self::FILTER_FUNCTION));
	}
	
	public static function bindClass($key, $filter, $priority = 10, 
			$superKey = null) {

		return (self::bind($key, $filter, $priority, $superKey, self::FILTER_CLASS));
	}

	public static function bindLib($key, $filter, $priority = 10, 
			$superKey = null) {

		return (self::bind($key, $filter, $priority, $superKey, self::FILTER_LIB));
	}

	public static function get($key) {
		list($key, $priority, $filter) = self::decodeKey($key);
		
		return (isset(self::$Filters[$key][$priority][$filter])
				? self::$Filters[$key][$priority][$filter] : false); 
	}

	public static function getBySuperKey($superKey) {
		if (isset(self::$_SuperKeys[$superKey])) {
			list($key, $priority, $filter) = self::$_SuperKeys[$superKey];
			return (isset(self::$Filters[$key][$priority][$filter])
					? self::$Filters[$key][$priority][$filter] : false); 
		} else {
			return false;
		}
	}
	
	public static function getByPriority($key, $priority) {
		return (isset(self::$Filters[$key][$priority])
				? self::$Filters[$key][$priority] : false);
	}
	
	public static function getByKey($key) {
		return (isset(self::$Filters[$key]) ? self::$Filters[$key] : false);
	}

	public static function getAll() {
		return self::$Filters;
	}
	
	public static function remove($key) {
		list($key, $priority, $filter) = self::decodeKey($key);
		unset(self::$Filters[$key][$priority][$filter]);
		
		return true; 
	}
	
	public static function removeBySuperKey($superKey) {
		if (isset(self::$_SuperKeys[$superKey])) {
			list($key, $priority, $filter) = self::$_SuperKeys[$superKey];
			
			return self::remove($key, $priority, $filter);
		} else {
			return false;
		}
	}

	public static function removeByPriority($key, $priority) {
		unset(self::$Filters[$key][$priority]);
		
		return true;
	}
	
	public static function removeByKey($key) {
		unset(self::$Filters[$key]);
			
		return true;
	}

	public static function removeAll() {
		self::$Filters = array();
		
		return true;
	}

	public static function bindSuperKey($superKey, $key) {
		list($key, $priority, $filter) = self::decodeKey($key);
		self::$_SuperKeys[$superKey] = array($key, $priority, $filter);
		
		return true;
	}
	
	public static function getInfoBySuperKey($superKey) {
		return (isset(self::$_SuperKeys[$superKey]) ? self::$_SuperKeys[$superKey] : false);
	}
	
	public static function getAllInfosBySuperKey() {
		return self::$_SuperKeys;
	}
	
	public static function removeSuperKey($superKey) {
		unset(self::$_SuperKeys[$superKey]);
		return true;
	}
	
	public static function removeAllSuperKeys() {
		self::$_SuperKeys = array();
		
		return true;
	}

	public static function merge($mainKey, $mergeKey, 
			$priority = 101) {
		
		self::$Filters[$mainKey][$priority][".$mergeKey"] = array(
			0 => self::FILTER_MERGE,
			1 => $mergeKey
		);
		
		return true;
	}
	
	public static function getMergedFilter($mainKey, $mergeKey, 
			$priority = 101) {
		
		return (isset(self::$Filters[$key][$priority][".$mergeKey"])
				? self::$Filters[$key][$priority][".$mergeKey"] : false); 
	}
	
	public static function removeMergedFilter($mainKey, $mergeKey, 
			$priority = 101) {

		unset(self::$Filters[$mainKey][$priority][".$mergeKey"]);
		
		return true;
	}
	
	public static function apply($key, $data, $params = array()) {
		if (isset(self::$Filters[$key])) {
			$allFilters = self::$Filters[$key];
			ksort($allFilters);

			foreach ($allFilters as $filters) {
				foreach ($filters as $filter) {
					switch ($filter[0]) {
						case self::FILTER_OBJECT :
							$object = "F{$filter[1]}";
							$data   = self::_applyObject($object, array($data, $params));
							break;
						
						case self::FILTER_FUNCTION :
							$data = self::_applyFunction($filter[1], array($data, $params));
							break;
						
						case self::FILTER_CLASS :
							list($class, $method) = explode('.', $filter[1]);
							$data = self::_applyClass($class, $method, array($data, $params));
							break;
							
						case self::FILTER_LIB :
							list($lib, $method) = explode('::', $filter[1]);
							$data = self::_applyLib($lib, $method, array($data, $params));
							break;
						
						case self::FILTER_MERGE :
							$data = self::apply($filter[1], $data, $params);
							break;

						default :
							break;
					}
				}
			}
		}

		self::$_Applied[$key] = true;
		
		return $data;
	}
	
	public static function isApplied($key) {
		return (isset(self::$_Applied[$key]) && self::$_Applied[$key]);
	}
	
	protected static function _applyObject($object, $params) {
		return call_user_func_array(array($object,'filter'), $params);
	}
	
	protected static function _applyFunction($function, $params) {
		return call_user_func_array($function, $params);
	}
	
	protected static function _applyClass($class, $method, $params) {
		$instance = Factory::getInstance($class, false);
		return call_user_func_array(array($instance, $method), $params);
	}
	
	protected static function _applyLib($lib, $method, $params) {
		return call_user_func_array(array($lib, $method), $params);
	}
}

